#ifndef SCT_PLAYER_PLAYER_H
#define SCT_PLAYER_PLAYER_H

#include <libxml/parser.h>
#include <libxml/xpath.h>

#include "sct/core/common.h"
#include "sct/core/thread.h"
#include "sct/framework/object.h"

#include "sct/rtspclient/rtspclientinterface.h"
#include "sct/demux/tsdemux.h"
#include "sct/decode/decodeinterface.h"
#include "sct/httpclient/httpclientinterface.h"

#include "window.h"
#include "audiooutput.h"
#include "renderinterface.h"

namespace sct
{

class CPlayPipeLine;
class CPlayer: public CRTSPClientCallback,
        public CThreadCallBack,
        public CTsDemuxCallback,
        public CObject
{
public:
    CPlayer();
    virtual ~CPlayer();

protected:
    virtual void onRecvData(CRTSPClientInterface* caller, const void* d,
            const int len,
            const CRTSPClientInterface::CBlockInfo & info);

    virtual int main(CThread * caller);
    virtual void onRecvPesPacket(CTsDemux* caller, const CTsDemux::CPes * pes);
    virtual void onPcrUpdate(CTsDemux* caller, const CTsDemux::CPmt* pmt);
    virtual void onTimerTimeout(const int id);

private:
    enum State
    {
        Initing = SCT_ENUM_OFFSET,
        Running,
        Terminated,
    };

private:
    Window * mWin;
    CAudioOutput * mAudioOutput;

    CRTSPClientInterface * mRtspClient;
    CThread * mRecvThd;
    State mState;
    CTsDemux * mDemux;

    CDecodeInterface * mVideoDecode;
    CDecodeInterface * mAudioDecode;

    CPlayPipeLine * mVideoPlayPipeLine;
    CPlayPipeLine * mAudioPlayPipeLine;

    CHttpClientInterface * mHttpClient;
    uint8_t* mHttpBuff;
    int mHttpBuffMaxSz;

    int mTimerBootup;
    int mTimerPeriod;
    CThread * mObjectLoop;
    int mStreamId;
};

/**
 * @brief The CPlayPipeLine class
 *
 * will create a thread to control play
 */
class CPlayPipeLine: public CDecodeCallback, public CThreadCallBack
{
public:
    enum Type
    {
        Video = SCT_ENUM_OFFSET,
        Audio,
    };

public:
    CPlayPipeLine();
    virtual ~CPlayPipeLine();

    void setType(const Type type);
    void setDecoder(CDecodeInterface * decoder);
    void setRender(CRenderInterface * render);

    /**
     * @brief start play
     */
    void init();

    /**
     * @brief updateSyncClock
     * @param systemClk
     * @param streamPcr in micro second
     */
    void updateSyncClock(const int64_t systemClk, const int64_t streamPcr);
    /**
     * @brief pushEsData
     * @param d
     * @param len
     * @param dts in micro second
     * @param pts in micro second
     */
    void pushEsData(const uint8_t * d, const int len, const int64_t dts, const int64_t pts);

    /**
     * @brief updateSyncValue
     * @param value + means play faster, - means play slow
     */
    void updateSyncValue(const int64_t value);
protected:
    virtual void onGenData(CDecodeInterface* caller,
            const void* buf, const int len,
            const CDecodeInterface::CBlockInfo & info);

    virtual int main(CThread * caller);

private:
    enum State
    {
        Initing = SCT_ENUM_OFFSET,
        Running,
        Terminated,
    };

    class CStreamData
    {
    public:
        int64_t pts;
        int64_t dts;

        int width;
        int height;

        uint8_t * data;
        int len;

        int sample_rate;

    public:
        static void free(CStreamData* item)
        {
            SCT_DELETE_ARRAY(item->data);
            SCT_DELETE(item);
        }
    };

private:
    void decodeControl();
    int64_t toSyncClock(const int64_t clk);

    void renderControl();
private:
    Type mType;

    CMutex * mMutex;

    State mDecodeState;
    CThread * mDecodeThread;

    State mRenderState;
    CThread * mRenderThread;

    std::list<CStreamData*> mEsDatas;
    std::list<CStreamData*> mRawDatas;

    CDecodeInterface * mDecoder;
    CRenderInterface * mRender;

    bool mSync;
    int64_t mSyncSystemClock;
    int64_t mSyncStreamPcr;

    int64_t mSyncValue;

};
}
#endif
